
package com.hlkj.pay.app.merchant.ext.impl.helipay;
/*
 * Hlpay-Plus aggregate payment system.
 * Copyright (c) 2024-2025 Hlpay Team Copyright has the right of final interpretation.
 */

import java.time.LocalDateTime;
import java.util.*;

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import com.hlkj.framework.common.pojo.CommonResult;
import com.hlkj.framework.common.util.json.JsonUtils;
import com.hlkj.framework.extension.annotation.Extension;
import com.hlkj.pay.app.merchant.ext.ChannelMerchantSettleExtPt;
import com.hlkj.pay.app.merchant.ext.MerchantExtConstant;
import com.hlkj.pay.app.merchant.ext.dto.settle.ChannelMerchantAccessSettleContext;
import com.hlkj.pay.app.merchant.ext.impl.helipay.constant.HeliPayConstant;
import com.hlkj.pay.app.merchant.ext.impl.helipay.dto.HeliPayConfig;
import com.hlkj.pay.app.merchant.ext.impl.helipay.dto.res.HeliPayMerchantBalanceRes;
import com.hlkj.pay.app.merchant.ext.impl.helipay.dto.res.HeliPayMerchantSettlementQueryRes;
import com.hlkj.pay.app.merchant.ext.impl.helipay.dto.res.HeliPayOrderCommonRes;
import com.hlkj.pay.app.merchant.ext.impl.helipay.handler.HeliPayMerchantCommonParamsHandle;
import com.hlkj.pay.app.merchant.ext.impl.helipay.util.HeliPaySM2Utils;
import com.hlkj.pay.common.MerchantResultCode;
import com.hlkj.pay.dto.merchant.access.MerchantAccessBalanceDto;
import com.hlkj.pay.dto.order.notify.OrderSettleNotifyDto;
import com.hlkj.pay.enums.MerchantEnum;
import com.hlkj.pay.enums.PayOrderEnums;
import com.hlkj.pay.infrastructure.model.merchant.MerchantAccessInfoDO;
import com.hlkj.pay.infrastructure.model.order.OrderSettleDO;
import com.hlkj.pay.infrastructure.model.order.PayOrderDO;
import com.hlkj.pay.infrastructure.model.pay.PayChannelRequestDO;
import com.hlkj.pay.infrastructure.model.pay.ProviderChannelConfigDO;
import com.hlkj.pay.util.DateUtils;
import com.hlkj.pay.util.HttpRequestUtil;
import com.hlkj.pay.util.MerchantUtils;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * @author HlpayTeam
 * @date 2024/12/29 17:40
 */
@Slf4j
@Repository
@RequiredArgsConstructor
@Extension(bizId = MerchantExtConstant.CHANNEL_HELIPAY)
public class HeliPayMerchantSettleExtPt implements ChannelMerchantSettleExtPt {

    @Autowired
    private HeliPayMerchantCommonParamsHandle heliPayMerchantCommonParamsHandle;

    public static Set<String> settleOrderSignFields;

    @Override
    public void queryAccessBalance(ChannelMerchantAccessSettleContext merchantAccessAuthQueryContext) {
        CommonResult<HeliPayConfig> heliPayConfigCommonResult = heliPayMerchantCommonParamsHandle.heliPayConfig(merchantAccessAuthQueryContext.getProviderChannelConfig(),
                merchantAccessAuthQueryContext.isTest());
        // 配置转换
        if (!heliPayConfigCommonResult.isSuccess()) {
            merchantAccessAuthQueryContext.setRemark(heliPayConfigCommonResult.getMsg());
            merchantAccessAuthQueryContext.setProcessStatus(Boolean.FALSE);
            return;
        }
        HeliPayConfig heliPayConfig = heliPayConfigCommonResult.getData();
        // 1. 数据初始化
        MerchantAccessInfoDO merchantAccessInfo = merchantAccessAuthQueryContext.getMerchantAccessInfo();
        try {
            String accessUrl = heliPayMerchantCommonParamsHandle.accessAccountUrl(merchantAccessAuthQueryContext.isTest());
            Map<String, String> params = new LinkedHashMap<>();
            params.put("P1_bizType", "MerchantAccountQuery");
            params.put("P2_customerNumber", merchantAccessInfo.getSubMchId());
            params.put("P3_timestamp", DateUtils.dateToString(System.currentTimeMillis(), "yyyyMMddHHmmss"));
            CommonResult<String> resultResult = heliPayMerchantCommonParamsHandle.heliPayAccountRequest(accessUrl, heliPayConfig, params, null);
            log.info("合利宝 商户余额查询 返回参数 :{}", JsonUtils.toJsonString(resultResult));
            if (!resultResult.isSuccess()) {
                merchantAccessAuthQueryContext.setRemark("合利宝 商户余额查询 请求失败,失败信息" + resultResult.getMsg());
                merchantAccessAuthQueryContext.setProcessStatus(Boolean.FALSE);
                return;
            }
            String response = resultResult.getData();
            try {
                HeliPayMerchantBalanceRes heliPayOrderCommonRes = JsonUtils.parseObject(response, HeliPayMerchantBalanceRes.class);
                if (HeliPayConstant.REQUEST_SUCCESS.equals(heliPayOrderCommonRes.getRt2_retCode())) {
                    PayOrderDO updartePayOrderDO = new PayOrderDO();
                    updartePayOrderDO.setState(PayOrderEnums.PAY_ORDER_STATUS.CLOSE.getCode());

                    MerchantAccessBalanceDto merchantAccessBalanceDto = new MerchantAccessBalanceDto();
                    // AVAILABLE("正常",1),
                    // FROZEN("冻结",2),
                    // FREEZE_DEBIT("冻结出帐",3),
                    // FREEZE_CREDIT("冻结入账",4),
                    // CANCELLED("已注销",5);
                    switch (heliPayOrderCommonRes.getRt5_accountStatus()) {
                        case "AVAILABLE":
                            merchantAccessBalanceDto.setAccountStatus(1);
                            break;
                        case "FROZEN":
                            merchantAccessBalanceDto.setAccountStatus(2);
                            break;
                        case "FREEZE_DEBIT":
                            merchantAccessBalanceDto.setAccountStatus(3);
                            break;
                        case "FREEZE_CREDIT":
                            merchantAccessBalanceDto.setAccountStatus(4);
                            break;
                        case "CANCELLED":
                            merchantAccessBalanceDto.setAccountStatus(5);
                            break;
                    }
                    merchantAccessBalanceDto.setBalance(heliPayOrderCommonRes.getRt6_balance());
                    merchantAccessBalanceDto.setFrozenBalance(heliPayOrderCommonRes.getRt7_frozenBalance());
                    merchantAccessBalanceDto.setRechargeBalance(heliPayOrderCommonRes.getRt14_rechargeBalance());
                    merchantAccessBalanceDto.setAmountToBeSettled(heliPayOrderCommonRes.getRt15_amountToBeSettled());
                    merchantAccessAuthQueryContext.setMerchantAccessBalanceDto(merchantAccessBalanceDto);
                }
                else {
                    String respDesc = heliPayOrderCommonRes.getRt3_retMsg();
                    merchantAccessAuthQueryContext.setProcessStatus(Boolean.FALSE);
                    if (StringUtils.hasText(respDesc)) {
                        merchantAccessAuthQueryContext.setRemark(respDesc);
                    }
                    else {
                        merchantAccessAuthQueryContext.setRemark("合利宝 商户余额查询 返回错误信息为空,错误码为:" + heliPayOrderCommonRes.getRt2_retCode());
                    }
                }
            }
            catch (Exception e) {
                e.printStackTrace();
                merchantAccessAuthQueryContext.setProcessStatus(Boolean.FALSE);
                merchantAccessAuthQueryContext.setRemark("合利宝 商户余额查询 请求失败,失败信息:" + response);
            }
        }
        catch (Exception e) {
            e.printStackTrace();
            log.error("合利宝 商户余额查询 请求失败,失败信息 message: {}", e.getMessage());
            merchantAccessAuthQueryContext.setRemark("合利宝 商户余额查询 请求失败,失败信息" + e.getMessage());
            merchantAccessAuthQueryContext.setProcessStatus(Boolean.FALSE);
        }
    }

    @Override
    public void submitAccessSettle(ChannelMerchantAccessSettleContext channelMerchantAccessSettleContext) {
        // 配置转换
        CommonResult<HeliPayConfig> commonResult = heliPayMerchantCommonParamsHandle.heliPayConfig(channelMerchantAccessSettleContext.getProviderChannelConfig(),
                channelMerchantAccessSettleContext.isTest());
        HeliPayConfig heliPayConfig = commonResult.getData();
        OrderSettleDO accessAmountOrderDO = channelMerchantAccessSettleContext.getOrderSettleDO();
        MerchantAccessInfoDO merchantAccessInfo = channelMerchantAccessSettleContext.getMerchantAccessInfo();
        try {
            String payUrl = heliPayMerchantCommonParamsHandle.settleAccountUrl(channelMerchantAccessSettleContext.isTest());
            LinkedHashMap<String, Object> params = new LinkedHashMap<>();
            LinkedHashMap<String, Object> noSignFieldMap = new LinkedHashMap<>();
            params.put("P1_bizType", "MerchantSettlement");
            params.put("P2_orderId", accessAmountOrderDO.getPayOrderNo());
            params.put("P3_customerNumber", merchantAccessInfo.getSubMchId());
            params.put("P4_amount", MerchantUtils.bigDecimalToString2(accessAmountOrderDO.getAmount()));
            if (StringUtils.hasText(accessAmountOrderDO.getRemark())) {
                params.put("P5_summary", accessAmountOrderDO.getRemark());
            }
            else {
                params.put("P5_summary", "");
            }
            params.put("P6_notifyUrl", channelMerchantAccessSettleContext.getNotifyUrl());
            // noSignFieldMap.put("P6_notifyUrl", "");
            PayChannelRequestDO payChannelRequestDO = new PayChannelRequestDO();
            channelMerchantAccessSettleContext.setPayChannelRequestDO(payChannelRequestDO);
            payChannelRequestDO.setRequestParams(JsonUtils.toJsonString(params));
            payChannelRequestDO.setRequestUrl(payUrl);
            log.info("合利宝 提交结算 请求参数:{}", JsonUtils.toJsonString(params));
            CommonResult<String> resultResult = heliPayMerchantCommonParamsHandle.heliPayCommonRequest(payUrl, heliPayConfig, params, noSignFieldMap);
            log.info("合利宝 提交结算 返回参数:{}", JsonUtils.toJsonString(resultResult));
            payChannelRequestDO.setResponse(resultResult.getData());
            if (!resultResult.isSuccess()) {
                channelMerchantAccessSettleContext.setRemark("合利宝提交结算请求失败,失败信息" + resultResult.getMsg());
                channelMerchantAccessSettleContext.setProcessStatus(Boolean.FALSE);
                return;
            }
            String response = resultResult.getData();
            HeliPayOrderCommonRes heliPayOrderCommonRes = JsonUtils.parseObject(response, HeliPayOrderCommonRes.class);

            OrderSettleDO updatePayOrderDO = new OrderSettleDO();
            channelMerchantAccessSettleContext.setUpdateAccessAmountOrderDO(updatePayOrderDO);
            if (HeliPayConstant.REQUEST_SUCCESS.equals(heliPayOrderCommonRes.getRt2_retCode())) {
                updatePayOrderDO.setOrderStatus(MerchantEnum.MERCHANT_ACCESS_AMOUNT_STATUS.DOING.getCode());
            }
            else {
                String respDesc = heliPayOrderCommonRes.getRt3_retMsg();
                updatePayOrderDO.setOrderStatus(MerchantEnum.MERCHANT_ACCESS_AMOUNT_STATUS.FAIL.getCode());
                if (StringUtils.hasText(respDesc)) {
                    updatePayOrderDO.setRemark(respDesc);
                }
                else {
                    updatePayOrderDO.setRemark("合利宝提交结算返回错误信息为空,错误码为:" + heliPayOrderCommonRes.getRt2_retCode());
                }
            }
        }
        catch (Exception e) {
            e.printStackTrace();
            log.error("合利宝提交结算请求失败 message:{}", e.getMessage(), e);
            channelMerchantAccessSettleContext.setRemark("合利宝提交结算请求失败,失败信息" + e.getMessage());
            channelMerchantAccessSettleContext.setProcessStatus(Boolean.FALSE);
        }
    }

    @Override
    public void queryAccessSettle(ChannelMerchantAccessSettleContext channelMerchantAccessSettleContext) {
        // 配置转换
        CommonResult<HeliPayConfig> commonResult = heliPayMerchantCommonParamsHandle.heliPayConfig(channelMerchantAccessSettleContext.getProviderChannelConfig(),
                channelMerchantAccessSettleContext.isTest());
        HeliPayConfig heliPayConfig = commonResult.getData();
        OrderSettleDO orderSettleDO = channelMerchantAccessSettleContext.getOrderSettleDO();
        MerchantAccessInfoDO merchantAccessInfo = channelMerchantAccessSettleContext.getMerchantAccessInfo();
        try {
            String payUrl = heliPayMerchantCommonParamsHandle.settleAccountUrl(channelMerchantAccessSettleContext.isTest());
            LinkedHashMap<String, Object> params = new LinkedHashMap<>();
            params.put("P1_bizType", "MerchantSettlementQuery");
            params.put("P2_orderId", orderSettleDO.getPayOrderNo());
            params.put("P3_customerNumber", merchantAccessInfo.getSubMchId());
            Long time = DateUtils.shanghaiStartDate(LocalDateTime.now().plusDays(-1));
            params.put("P4_settleDate", DateUtils.dateToString(time, "yyyyMMdd"));
            log.info("合利宝 结算查询 请求参数:{}", JsonUtils.toJsonString(params));
            CommonResult<String> resultResult = heliPayMerchantCommonParamsHandle.heliPayCommonRequest(payUrl, heliPayConfig, params, null);
            log.info("合利宝 结算查询 返回参数:{}", JsonUtils.toJsonString(resultResult));
            if (!resultResult.isSuccess()) {
                channelMerchantAccessSettleContext.setRemark("合利宝结算查询请求失败,失败信息" + resultResult.getMsg());
                channelMerchantAccessSettleContext.setProcessStatus(Boolean.FALSE);
                return;
            }
            String response = resultResult.getData();
            HeliPayMerchantSettlementQueryRes heliPayMerchantSettlementQueryRes = JsonUtils.parseObject(response, HeliPayMerchantSettlementQueryRes.class);

            OrderSettleDO updatePayOrderDO = new OrderSettleDO();
            channelMerchantAccessSettleContext.setUpdateAccessAmountOrderDO(updatePayOrderDO);
            if (HeliPayConstant.REQUEST_SUCCESS.equals(heliPayMerchantSettlementQueryRes.getRt2_retCode())) {
                updatePayOrderDO.setOrderStatus(MerchantEnum.MERCHANT_ACCESS_AMOUNT_STATUS.DOING.getCode());
                String rt4SettleRecord = heliPayMerchantSettlementQueryRes.getRt4_settleRecords();
                List<HeliPayMerchantSettlementQueryRes.SettleRecord> rt4SettleRecords = JsonUtils.parseArray(rt4SettleRecord, HeliPayMerchantSettlementQueryRes.SettleRecord.class);
                if (rt4SettleRecords == null || rt4SettleRecords.size() == 0) {
                    updatePayOrderDO.setOrderStatus(MerchantEnum.MERCHANT_ACCESS_AMOUNT_STATUS.DOING.getCode());
                }else {
                    HeliPayMerchantSettlementQueryRes.SettleRecord settleRecord = rt4SettleRecords.get(0);
                    if (settleRecord != null) {
                        switch (settleRecord.getOrderStatus()) {
                            case "DONE":
                                updatePayOrderDO.setOrderStatus(MerchantEnum.MERCHANT_ACCESS_AMOUNT_STATUS.SUCCESS.getCode());
                                break;
                            case "FAILED":
                                updatePayOrderDO.setOrderStatus(MerchantEnum.MERCHANT_ACCESS_AMOUNT_STATUS.FAIL.getCode());
                                break;
                            case "MANUAL":
                                updatePayOrderDO.setOrderStatus(MerchantEnum.MERCHANT_ACCESS_AMOUNT_STATUS.MANUAL.getCode());
                                break;
                            default:
                                break;
                        }
                        updatePayOrderDO.setRemark(settleRecord.getReason());
                        if (StringUtils.hasText(settleRecord.getCompleteDate())) {
                            try {
                                updatePayOrderDO.setSuccessTime(DateUtils.shanghaiDate(settleRecord.getCompleteDate(), "yyyy-MM-dd HH:mm:ss"));
                            }
                            catch (Exception e) {
                                log.error("合利宝订单查询返回 时间格式化错误 dateTime:{},message:{}", settleRecord.getCompleteDate(), e.getMessage());
                            }
                        }
                        if (StringUtils.hasText(settleRecord.getSettleFee())) {
                            updatePayOrderDO.setSettleFee(MerchantUtils.stringToBigDecimal(settleRecord.getSettleFee()));
                        }
                        if (StringUtils.hasText(settleRecord.getSettleDate())) {
                            try {
                                updatePayOrderDO.setSettleDate(DateUtils.shanghaiDate(settleRecord.getSettleDate(), "yyyy-MM-dd"));
                            }
                            catch (Exception e) {
                                log.error("合利宝订单查询返回 时间格式化错误 dateTime:{},message:{}", settleRecord.getSettleDate(), e.getMessage());
                            }
                        }
                        if (StringUtils.hasText(settleRecord.getSettleFee())) {
                            updatePayOrderDO.setSettleFee(MerchantUtils.stringToBigDecimal(settleRecord.getSettleFee()));
                        }
                    }
                }
            }
            else {
                String respDesc = heliPayMerchantSettlementQueryRes.getRt3_retMsg();
                updatePayOrderDO.setOrderStatus(MerchantEnum.MERCHANT_ACCESS_AMOUNT_STATUS.FAIL.getCode());
                if (StringUtils.hasText(respDesc)) {
                    updatePayOrderDO.setRemark(respDesc);
                }
                else {
                    updatePayOrderDO.setRemark("合利宝结算查询返回错误信息为空,错误码为:" + heliPayMerchantSettlementQueryRes.getRt2_retCode());
                }
            }
        }
        catch (Exception e) {
            e.printStackTrace();
            log.error("合利宝结算查询请求失败 message:{}", e.getMessage(), e);
            channelMerchantAccessSettleContext.setRemark("合利宝结算查询请求失败,失败信息" + e.getMessage());
            channelMerchantAccessSettleContext.setProcessStatus(Boolean.FALSE);
        }
    }

    @Override
    public CommonResult<OrderSettleNotifyDto> orderStatusParse(HttpServletRequest request, ProviderChannelConfigDO providerChannelConfigDO) {
        String response = HttpRequestUtil.requestBodyParams(request);
        log.info("合利宝收到结算回调,参数数据为 response:{}", response);
        String paramsConfig = providerChannelConfigDO.getParamsConfig();
        OrderSettleNotifyDto orderSettleNotifyDto=new OrderSettleNotifyDto();
        CommonResult<OrderSettleNotifyDto> payOrderDOCommonResult = CommonResult.success();
        try {
            if (CollectionUtils.isEmpty(settleOrderSignFields)) {
                settleOrderSignFields = new LinkedHashSet<>(Arrays.asList(HeliPayConstant.SETTLE_ORDER_NOTIFY_FIELD.split(",")));
            }
            CommonResult<HeliPayConfig> commonResult = heliPayMerchantCommonParamsHandle.heliPayConfig(providerChannelConfigDO, false);
            // 配置转换
            HeliPayConfig heliPayConfig = commonResult.getData();
            boolean verify = HeliPaySM2Utils.verify(response, heliPayConfig, settleOrderSignFields);
            if (!verify) {
                log.info("合利宝结算回调验签不通过,signature:{}", response);
                payOrderDOCommonResult.setCode(MerchantResultCode.MERCHANT_CHANNEL_CALLBACK_SIGN_ERROR.getCode());
            }
        }
        catch (Exception e) {
            e.printStackTrace();
            payOrderDOCommonResult.setCode(MerchantResultCode.MERCHANT_CHANNEL_CALLBACK_SIGN_ERROR.getCode());
        }
        HeliPayMerchantSettlementQueryRes heliPayMerchantSettlementQueryRes = JsonUtils.parseObject(response, HeliPayMerchantSettlementQueryRes.class);
        orderSettleNotifyDto.setCallBackBody(response);
        OrderSettleDO accessAmountOrderDO = new OrderSettleDO();
        orderSettleNotifyDto.setOrderSettleDO(accessAmountOrderDO);
        accessAmountOrderDO.setOrderStatus(MerchantEnum.MERCHANT_ACCESS_AMOUNT_STATUS.DOING.getCode());
        String rt4SettleRecord = heliPayMerchantSettlementQueryRes.getRt4_settleRecords();
        List<HeliPayMerchantSettlementQueryRes.SettleRecord> rt4SettleRecords = JsonUtils.parseArray(rt4SettleRecord, HeliPayMerchantSettlementQueryRes.SettleRecord.class);
        HeliPayMerchantSettlementQueryRes.SettleRecord settleRecord = rt4SettleRecords.get(0);
        if (settleRecord != null) {
            accessAmountOrderDO.setPayOrderNo(settleRecord.getOrderId());
            switch (settleRecord.getOrderStatus()) {
                case "DONE":
                    accessAmountOrderDO.setOrderStatus(MerchantEnum.MERCHANT_ACCESS_AMOUNT_STATUS.SUCCESS.getCode());
                    break;
                case "FAILED":
                    accessAmountOrderDO.setOrderStatus(MerchantEnum.MERCHANT_ACCESS_AMOUNT_STATUS.FAIL.getCode());
                    break;
                case "MANUAL":
                    accessAmountOrderDO.setOrderStatus(MerchantEnum.MERCHANT_ACCESS_AMOUNT_STATUS.MANUAL.getCode());
                    break;
                default:
                    break;
            }
            accessAmountOrderDO.setRemark(settleRecord.getReason());
            if (StringUtils.hasText(settleRecord.getCompleteDate())) {
                try {
                    accessAmountOrderDO.setSuccessTime(DateUtils.shanghaiDate(settleRecord.getCompleteDate(), "yyyy-MM-dd HH:mm:ss"));
                }
                catch (Exception e) {
                    log.error("合利宝结算回调返回 时间格式化错误 dateTime:{},message:{}", settleRecord.getCompleteDate(), e.getMessage());
                }
            }
            if (StringUtils.hasText(settleRecord.getSettleFee())) {
                accessAmountOrderDO.setSettleFee(MerchantUtils.stringToBigDecimal(settleRecord.getSettleFee()));
            }
            if (StringUtils.hasText(settleRecord.getSettleDate())) {
                try {
                    accessAmountOrderDO.setSettleDate(DateUtils.shanghaiDate(settleRecord.getSettleDate(), "yyyy-MM-dd"));
                }
                catch (Exception e) {
                    log.error("合利宝订单查询返回 时间格式化错误 dateTime:{},message:{}", settleRecord.getSettleDate(), e.getMessage());
                }
            }
        }
        payOrderDOCommonResult.setData(orderSettleNotifyDto);
        log.info("合利宝收到结算回调,解析后的订单数据为 payOrderDO:{}", JsonUtils.toJsonString(accessAmountOrderDO));
        return payOrderDOCommonResult;
    }
}
